﻿using System;
using Xamarin.Forms;
using System.Linq;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Input;

namespace DLToolkit.Forms.Controls
{
	// used part of the code of: https://github.com/conceptdev/xamarin-forms-samples/blob/master/Evolve13/Evolve13/Controls/WrapLayout.cs
	public class TagEntryView : Layout<View>, IDisposable
	{
		public TagEntryView()
		{
			PropertyChanged += TagEntryViewPropertyChanged;
			PropertyChanging += TagEntryViewPropertyChanging;
		}

		public event EventHandler<ItemTappedEventArgs> TagTapped;

		internal void PerformTagTap(object item)
		{
            TagTapped?.Invoke(this, new ItemTappedEventArgs(null, item));

            var command = TagTappedCommand;
			if (command != null && command.CanExecute(item))
			{
				command.Execute(item);
			}
		}

		public Func<View> TagViewFactory { get; set; }

        public static BindableProperty TagTappedCommandProperty = BindableProperty.Create(nameof(TagTappedCommand), typeof(ICommand), typeof(TagEntryView), null);

		public ICommand TagTappedCommand
		{
			get { return (ICommand)GetValue(TagTappedCommandProperty); }
			set { SetValue(TagTappedCommandProperty, value); }
		}

		public static readonly BindableProperty TagItemsProperty = BindableProperty.Create(nameof(TagItems), typeof(IList), typeof(TagEntryView), default(IList), BindingMode.TwoWay);

		public IList TagItems
		{
			get { return (IList)GetValue(TagItemsProperty); }
			set { SetValue(TagItemsProperty, value); }
		}

		public double Spacing
		{
            get;set;
		}


		private void TagEntryViewPropertyChanging(object sender, Xamarin.Forms.PropertyChangingEventArgs e)
		{
			if (e.PropertyName == TagItemsProperty.PropertyName)
			{
				var tagItems = TagItems as INotifyCollectionChanged;
				if (tagItems != null)
					tagItems.CollectionChanged -= TagItemsCollectionChanged;
			}
		}

		private void TagEntryViewPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName == TagItemsProperty.PropertyName)
			{
				var tagItems = TagItems as INotifyCollectionChanged;
				if (tagItems != null)
					tagItems.CollectionChanged += TagItemsCollectionChanged;

				ForceReload();
			}
		}

		private void TagItemsCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
		{
			ForceReload();
		}

		public void ForceReload()
		{
			Children.Clear();

			for (int i = 0; i < TagItems.Count; i++)
			{
				var view = TagViewFactory?.Invoke();
				view.BindingContext = TagItems[i];

				view.GestureRecognizers.Add(new TapGestureRecognizer(){
					Command = new Command(() => PerformTagTap(view.BindingContext))
				});

				Children.Add(view);
			}
		}

		private void OnSizeChanged()
		{
			this.ForceLayout();
		}

        protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
        {
            if (WidthRequest > 0)
                widthConstraint = Math.Min(widthConstraint, WidthRequest);
            if (HeightRequest > 0)
                heightConstraint = Math.Min(heightConstraint, HeightRequest);

            var internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
            var internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);

            return DoHorizontalMeasure(internalWidth, internalHeight);
        }

		private SizeRequest DoHorizontalMeasure(double widthConstraint, double heightConstraint)
		{
			var rowCount = 1;

			double width = 0;
			double height = 0;
			double minWidth = 0;
			double minHeight = 0;
			double widthUsed = 0;

			foreach (var item in Children)
			{
				var size = item.Measure(widthConstraint, heightConstraint);
				height = Math.Max (height, size.Request.Height);

				var newWidth = width + size.Request.Width + Spacing;
				if (newWidth > widthConstraint) {
					rowCount++;
					widthUsed = Math.Max(width, widthUsed);
					width = size.Request.Width;
				} else
					width = newWidth;

				minHeight = Math.Max(minHeight, size.Minimum.Height);
				minWidth = Math.Max (minWidth, size.Minimum.Width);
			}

			if (rowCount > 1) {
				width = Math.Max(width, widthUsed);
				height = (height + Spacing) * rowCount - Spacing; // via MitchMilam
			}

			return new SizeRequest(new Size(width, height), new Size(minWidth,minHeight));
		}

		protected override void LayoutChildren (double x, double y, double width, double height)
		{
			double rowHeight = 0;
			var yPos = y;
            var xPos = x;

            foreach (var child in Children.Where(c => c.IsVisible))
			{
				var request = child.Measure (width, height);

				var childWidth = request.Request.Width;
                var childHeight = request.Request.Height;

                rowHeight = Math.Max(rowHeight, childHeight);

				if (xPos + childWidth > width)
				{
					xPos = x;
					yPos += rowHeight + Spacing;
					rowHeight = 0;
				}

				var region = new Rectangle(xPos, yPos, childWidth, childHeight);
				LayoutChildIntoBoundingRegion(child, region);
				xPos += region.Width + Spacing;
			}
		}

        public void Dispose()
        {
            PropertyChanged -= TagEntryViewPropertyChanged;
            PropertyChanging -= TagEntryViewPropertyChanging;

            var tagItems = TagItems as INotifyCollectionChanged;
            if (tagItems != null)
            {
                tagItems.CollectionChanged -= TagItemsCollectionChanged;
            }

            GC.SuppressFinalize(this);
        }
    }
}

